/*
 * Copyright (C) 2023 ab_skywalker@163.com All Rights Reserved.
 *
 * SPDX-License-Identifier: MIT
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */
#include "drv_ad.h"
#include "stm32g4xx_hal.h"
#include <stdint.h>
#include <stdio.h>
#include "FreeRTOS.h"

uint16_t initadvalue[] = {0, 0, 0, 0, 0};
uint16_t curadvalue[] = {0, 0, 0, 0, 0};
ADC_HandleTypeDef hadcCurr;
DMA_HandleTypeDef hdmaCurr;

AD_CALLBACK_PTR adcConvCallback;

uint8_t CurrBiasCaptureCnt = 0;

void adInit(void)
{
    ADC_MultiModeTypeDef multimode = {0};
    ADC_ChannelConfTypeDef sConfig = {0};
    RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
    GPIO_InitTypeDef GPIO_InitStruct;
    __HAL_RCC_ADC12_CLK_ENABLE();
    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC12;
    PeriphClkInit.Adc12ClockSelection = RCC_ADC12CLKSOURCE_SYSCLK;
    HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
    //config ADC
    hadcCurr.Instance = ADC2;
    hadcCurr.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
    hadcCurr.Init.Resolution = ADC_RESOLUTION_12B;
    hadcCurr.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    hadcCurr.Init.GainCompensation = 0;
    hadcCurr.Init.ScanConvMode = ADC_SCAN_ENABLE;
    hadcCurr.Init.EOCSelection = ADC_EOC_SEQ_CONV;
    hadcCurr.Init.LowPowerAutoWait = DISABLE;
    hadcCurr.Init.ContinuousConvMode = DISABLE;
    hadcCurr.Init.NbrOfConversion = 5;
    hadcCurr.Init.DiscontinuousConvMode = DISABLE;
    hadcCurr.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T1_TRGO;
    hadcCurr.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
    hadcCurr.Init.DMAContinuousRequests = ENABLE;
    hadcCurr.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
    hadcCurr.Init.OversamplingMode = DISABLE;
    HAL_ADC_Init(&hadcCurr);

    HAL_ADCEx_Calibration_Start(&hadcCurr, ADC_SINGLE_ENDED);

    //config channel
    sConfig.Channel = ADC_CHANNEL_2;//U
    sConfig.Rank = ADC_REGULAR_RANK_1;
    sConfig.SamplingTime = ADC_SAMPLETIME_92CYCLES_5;
    sConfig.SingleDiff = ADC_SINGLE_ENDED;
    sConfig.OffsetNumber = ADC_OFFSET_NONE;
    sConfig.Offset = 0;
    HAL_ADC_ConfigChannel(&hadcCurr, &sConfig);

    sConfig.Channel = ADC_CHANNEL_14;//V
    sConfig.Rank = ADC_REGULAR_RANK_2;
    sConfig.SamplingTime = ADC_SAMPLETIME_92CYCLES_5;
    sConfig.SingleDiff = ADC_SINGLE_ENDED;
    sConfig.OffsetNumber = ADC_OFFSET_NONE;
    sConfig.Offset = 0;
    HAL_ADC_ConfigChannel(&hadcCurr, &sConfig);

    sConfig.Channel = ADC_CHANNEL_4;//W
    sConfig.Rank = ADC_REGULAR_RANK_3;
    sConfig.SamplingTime = ADC_SAMPLETIME_92CYCLES_5;
    sConfig.SingleDiff = ADC_SINGLE_ENDED;
    sConfig.OffsetNumber = ADC_OFFSET_NONE;
    sConfig.Offset = 0;
    HAL_ADC_ConfigChannel(&hadcCurr, &sConfig);

    sConfig.Channel = ADC_CHANNEL_1;//VBUS
    sConfig.Rank = ADC_REGULAR_RANK_4;
    sConfig.SamplingTime = ADC_SAMPLETIME_92CYCLES_5;
    sConfig.SingleDiff = ADC_SINGLE_ENDED;
    sConfig.OffsetNumber = ADC_OFFSET_NONE;
    sConfig.Offset = 0;
    HAL_ADC_ConfigChannel(&hadcCurr, &sConfig); 

    sConfig.Channel = ADC_CHANNEL_5;//NTP
    sConfig.Rank = ADC_REGULAR_RANK_5;
    sConfig.SamplingTime = ADC_SAMPLETIME_92CYCLES_5;
    sConfig.SingleDiff = ADC_SINGLE_ENDED;
    sConfig.OffsetNumber = ADC_OFFSET_NONE;
    sConfig.Offset = 0;
    HAL_ADC_ConfigChannel(&hadcCurr, &sConfig);
    
    //config gpio
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();

    GPIO_InitStruct.Pin = GPIO_PIN_1;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_11;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_0;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_4;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    __HAL_RCC_DMAMUX1_CLK_ENABLE();
    __HAL_RCC_DMA1_CLK_ENABLE();
    hdmaCurr.Instance = DMA1_Channel1;
    hdmaCurr.Init.Request = DMA_REQUEST_ADC2;
    hdmaCurr.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdmaCurr.Init.PeriphInc = DMA_PINC_DISABLE;
    hdmaCurr.Init.MemInc = DMA_MINC_ENABLE;
    hdmaCurr.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdmaCurr.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdmaCurr.Init.Mode = DMA_CIRCULAR;
    hdmaCurr.Init.Priority = DMA_PRIORITY_MEDIUM;
    HAL_DMA_Init(&hdmaCurr);

    __HAL_LINKDMA(&hadcCurr, DMA_Handle, hdmaCurr);

    //config nvic
    HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 2, 0);
    HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);

    HAL_ADC_Start_DMA(&hadcCurr, curadvalue, 5);
}

void adDeinit(void)
{
    HAL_ADC_Stop_DMA(&hadcCurr);
    HAL_ADC_DeInit(&hadcCurr);
    HAL_DMA_DeInit(&hdmaCurr);
    HAL_NVIC_DisableIRQ(DMA1_Channel1_IRQn);
}

void DMA1_Channel1_IRQHandler(void)
{
    HAL_DMA_IRQHandler(&hdmaCurr);
}

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
    if(hadc == &hadcCurr) {        
        if(adcConvCallback != NULL)
            adcConvCallback(curadvalue[0], curadvalue[1], curadvalue[2]);
    }
}

void drvAdCurrBiasCallback(int32_t ia, int32_t ib, int32_t ic)
{
    if(CurrBiasCaptureCnt < 16) {
        CurrBiasCaptureCnt++;
        initadvalue[0] += curadvalue[0];
        initadvalue[1] += curadvalue[1];
        initadvalue[2] += curadvalue[2];
    }
}

void drvAdCurrComputeBias(void)
{
    initadvalue[0] = initadvalue[0] >> 4;
    initadvalue[1] = initadvalue[1] >> 4;
    initadvalue[2] = initadvalue[2] >> 4;
    CurrBiasCaptureCnt = 0;
}

int32_t drvAdCurrCompute(int32_t vol, uint8_t channel)
{
    if(channel > 2)
        return 0;
    return ((vol - initadvalue[channel]) * 1598) / 1000;
}

void drvAdCurrCallbackRegister(AD_CALLBACK_PTR ptr)
{
    adcConvCallback = ptr;
}
